importScripts('../workers/sphericalMercator.js');
importScripts('../workers/geoutils.js');

//======================================================================================================================
//region constants
//======================================================================================================================

var tilePixels = new SphericalMercator({size: geoutils.grandTileSize});
var working = false;

var cols = geoutils.tileImgSize;
var rows = geoutils.tileImgSize;
var scaleFactor = 4;

/*
 * 16 subTile specs:
 * [[0, 128], [0, 128]]
 * [[128, 256], [0, 128]]
 * [[256, 384], [0, 128]]
 * ...
 */
var sixteenthPixelRanges = [];

for (var c = 0; c < scaleFactor; c++) {
    for (var r = 0; r < scaleFactor; r++) {
        //pixel ranges
        sixteenthPixelRanges.push([
            [r * (rows / scaleFactor - 1) + r, (r + 1) * rows / scaleFactor],
            [c * (cols / scaleFactor - 1) + c, (c + 1) * cols / scaleFactor]
        ]);
    }
}

//======================================================================================================================
//endregion
//======================================================================================================================

//======================================================================================================================
//region elevation mesh building
//======================================================================================================================

self.addEventListener('message', function (e) {
    // var time = Date.now();
    // console.time(time + ' worker');

    e = e.data;

    var pixels = e[0];
    var coords = e[1]; //terrain tile coord
    var tiles = e[2];  //requested imagery coords
    var parserIndex = e[3];
    var tileImgSize = e[4] || geoutils.tileImgSize || 512;

    var z = coords[0];
    var x = coords[1];
    var y = coords[2];
    //console.log(time+' started #'+parserIndex)

    var scale = 1;
    var offsetX = 0;
    var offsetY = 0;

    // var pow = Math.pow(2, z);
    // var scale = 1 / pow;

    var elevations = [];

    if (pixels) {
        /*
         * if the precision of the provided RGB tile is low, smooth it here
         */
        var blurPixels = pixels.data;
        //colors => elevations
        for (var e = 0; e < blurPixels.length; e += 4) {
            var R = blurPixels[e];
            var G = blurPixels[e + 1];
            var B = blurPixels[e + 2];
            elevations.push(-10000 + ((R * 256 * 256 + G * 256 + B) * 0.1));
        }
        var blurElevations = [];
        elevations = geoutils.applyElevationBlur(elevations, tileImgSize, blurElevations);
        /*
         * TODO: do smoothing on seams
         */
    } else {
        elevations = new Array(tileImgSize * tileImgSize * 4).fill(0);
    }

    // figure out tile coordinates of the 16 grandchildren of this tile
    var sixteenths = [];
    for (var c = 0; c < scaleFactor; c++) {
        for (var r = 0; r < scaleFactor; r++) {
            //tile coordinates
            sixteenths.push(slashify([z + 2, x * scaleFactor + c, y * scaleFactor + r]));
        }
    }


    //iterate through sixteenths...

    var tileSize = geoutils.basePlaneDimension / (Math.pow(2, z + 2));
    var vertices = geoutils.grandTileSize;
    var segments = vertices - 1;
    var segmentLength = tileSize / segments;

    var imagesDownloaded = 0;

    //check 16 grandchildren of this terrain tile
    sixteenths.forEach(function (d, i) {
        //if this grandchild is actually in view, proceed...
        if (tiles.indexOf(d) > -1) {
            imagesDownloaded++;
            // [z, x, y]
            d = deslash(d);

            var pxRange = sixteenthPixelRanges[i];

            var elev = [];
            //grab its elevations from the 4x4 grid
            for (var r = pxRange[0][0]; r < pxRange[0][1]; r++) {
                for (var c = pxRange[1][0]; c < pxRange[1][1]; c++) {
                    var currentPixelIndex = r * cols + c;
                    elev.push(elevations[currentPixelIndex]);
                }
            }

            var xOffset = (d[1] + 0.5) * tileSize - geoutils.basePlaneDimension / 2;
            var yOffset = (d[2] + 0.5) * tileSize - geoutils.basePlaneDimension / 2;

            var array = [];
            var dataIndex = 0;

            //iterate through rows
            for (var r = 0; r < vertices; r++) {

                var yPx = d[2] * geoutils.grandTileSize + r;
                var pixelLat = tilePixels.ll([x * tileSize, yPx], d[0])[1];  //latitude of this pixel
                var metersPerPixel = mPerPixel(pixelLat, tileSize, d[0]);   //real-world distance this pixel represents

                // y position of vertex in world pixel coordinates
                var yPos = -r * segmentLength + tileSize / 2;

                //iterate through columns
                for (var c = 0; c < vertices; c++) {
                    var xPos = c * segmentLength - tileSize / 2;
                    // array.push(xPos + xOffset, elev[dataIndex] / metersPerPixel, -yPos + yOffset);
                    array.push(xPos, elev[dataIndex] / metersPerPixel, -yPos);
                    dataIndex++;
                }
            }

            self.postMessage({
                makeMesh: [array, d],
                scale: scale,
                offsetX: xOffset * scale,
                offsetY: yOffset * scale,
            });
        }
    });

}, false);

//======================================================================================================================
//endregion
//======================================================================================================================

//======================================================================================================================
//region utils
//======================================================================================================================


function slashify(input) {
    return input.join('/');
}

function deslash(input) {
    return input.split('/').map(function (str) {
        return parseInt(str)
    });
}

function mPerPixel(latitude, tileSize, zoom) {
    return Math.abs(
        40075000 * Math.cos(latitude * Math.PI / 180) / (Math.pow(2, zoom) * tileSize)
    );
}


//======================================================================================================================
//endregion
//======================================================================================================================
